package com.corticon.dialogs.service;

import com.corticon.dialogs.dao.QuestionAvailabilityTypeDAO;

import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.corticon.dialogs.dao.QuestionDAO;
import com.corticon.dialogs.dao.QuestionSetDAO;
import com.corticon.dialogs.dao.QuestionStatusTypeDAO;
import com.corticon.dialogs.dao.QuestionnaireDAO;
import com.corticon.dialogs.dao.ResponseTypeDAO;
import com.corticon.dialogs.dao.UserDAO;
import com.corticon.dialogs.dto.QuestionSetDTO;
import com.corticon.dialogs.model.*;
import com.corticon.dialogs.util.QuestionUtils;
import com.corticon.dialogs.util.security.SecurityUtils;

/**
 * @author Lifan Yang
 * @since 2008-12-01
 */
@Service("questionnaireService")
public class QuestionnaireServiceImpl implements QuestionnaireService {

    @Resource
    private QuestionDAO questionDAO;
    @Resource
    private QuestionnaireDAO questionnaireDAO;
    @Resource
    private QuestionSetDAO questionSetDAO;
    @Resource
    private ResponseTypeDAO responseTypeDAO;
    @Resource
    private QuestionAvailabilityTypeDAO questionAvailabilityTypeDAO;
    @Resource
    private QuestionStatusTypeDAO questionStatusTypeDAO;
    @Resource
    private UserDAO userDAO;

    /**
     * This method is used to replicate the QuestionSets, Questions and other properties from a
     * Questionnaire to a new Questionnaire. After this action, a new Questionnaire, a number of
     * new QuestionSets and new Questions will be persisted into the Database.
     *
     * @param newQuestionnaire: This Questionnaire is from QuestionnaireController. Some fields of
     *                          this object are input by the user via web form.
     */
    @Transactional
    @Override
    public void replicate(Questionnaire newQuestionnaire, List<QuestionSetDTO> qsDTOList) throws
            IllegalAccessException, InvocationTargetException {

        Set<QuestionSetInNaire> questionSetsInNaire = new HashSet<QuestionSetInNaire>();

        // Get current user.
        User currentUser = userDAO.load(SecurityUtils.getCurrentUser().getUsername());

        for (QuestionSetDTO qsDTO : qsDTOList) {
            QuestionSet baseQuestionSet = questionSetDAO.findById(qsDTO.getId());

            // Generate a next refId for a new QuestionSet.
            String qsMaxRefId = questionSetDAO.getMaxRefId();
            String qsNextRefId;
            if (qsMaxRefId == null) {
                qsNextRefId = QuestionUtils.PAGE_REFID_PREFIX + QuestionUtils.REFID_BEGIN;
            } else {
                qsNextRefId = QuestionUtils.getNextRefId(qsMaxRefId);
            }

            // Create a new QuestionSet.
            QuestionSet newQuestionSet = new QuestionSet();
            BeanUtils.copyProperties(newQuestionSet, baseQuestionSet);

            // Reset some properties for new QuestionSet.
            newQuestionSet.setRefId(qsNextRefId);
            newQuestionSet.setVersion(1);
            newQuestionSet.setSibling(baseQuestionSet);
            newQuestionSet.setModifiedBy(currentUser);
            newQuestionSet.setQuesitonSetInNaires(null);
            newQuestionSet.setQuestionsInSet(new HashSet<QuestionInSet>());
            newQuestionSet.setAvailability(questionAvailabilityTypeDAO.load("open"));
            newQuestionSet.setStatus(questionStatusTypeDAO.load("active"));
            newQuestionSet.setRetiredBy(null);
            newQuestionSet.setRetiredNote(null);
            newQuestionSet.setRetiredOn(null);

            /*
            Loop all of the QuestionInSets for the replication of the Questions in the original
            QuestionSet.
             */
            for (QuestionInSet qis : baseQuestionSet.getQuestionsInSet()) {
                Question baseQuestion = qis.getQuestion();
                String qMaxRefId = questionDAO.findMaxRefId();
                String qNextRefId;

                // Generate the next ref id based on the current max ref id.
                if (qMaxRefId != null) {
                    qNextRefId = QuestionUtils.getNextRefId(qMaxRefId);
                } else {
                    qNextRefId = QuestionUtils.QUESTION_REFID_PREFIX + QuestionUtils.REFID_BEGIN;
                }

                Question newQuestion = new Question();
                BeanUtils.copyProperties(newQuestion, baseQuestion);

                // Reset some properties for new Question.
                newQuestion.setRefId(qNextRefId);
                newQuestion.setVersion(1);
                newQuestion.setSibling(baseQuestion);
                newQuestion.setModifiedBy(currentUser);
                newQuestion.setQuestionInSets(null);
                newQuestion.setQuestionAvailabilityType(questionAvailabilityTypeDAO.load("open"));
                newQuestion.setQuestionStatusType(questionStatusTypeDAO.load("active"));
                newQuestion.setRetiredBy(null);
                newQuestion.setRetiredNote(null);
                newQuestion.setRetiredOn(null);

                // Set new ResponseType for new Question.
                ResponseType baseResponseType = baseQuestion.getResponseType();
                if (baseQuestion.getResponseType().getName() != null) {
                    newQuestion.setResponseType(baseResponseType);
                } else {
                    ResponseType newResponseType = new ResponseType(baseResponseType.getUicontrol(),
                            baseResponseType.getDataType(), baseResponseType.getDataValidation(),
                            baseResponseType.getUnits(), baseResponseType.getFrequency(),
                            baseResponseType.getDynamic());

                    newResponseType.setModifiedBy(currentUser);

                    for (ResponseEntry oldRe : baseResponseType.getResponseEntries()) {
                        ResponseEntry newRe = new ResponseEntry(oldRe.getId(), oldRe.getLabel(),
                                oldRe.getSequence(), oldRe.getValue(), newResponseType);

                        newRe.setModifiedBy(currentUser);
                        newRe.setModifiedOn(new Date());
                        newResponseType.getResponseEntries().add(newRe);
                    }

                    // Save new ResponseType.
                    responseTypeDAO.save(newResponseType);

                    newQuestion.setResponseType(newResponseType);
                }

                // Save a new Question.
                questionDAO.save(newQuestion);

                // Replicate a new QuestionInSet.
                QuestionInSet newQis = new QuestionInSet(newQuestion, newQuestionSet, qis.getSequence(), qis.getPriority());
                newQis.setModifiedBy(currentUser);

                newQuestionSet.getQuestionsInSet().add(newQis);
            }

            // Save a new QuestionSet.
            questionSetDAO.save(newQuestionSet);

            // Replicate a new QuestionSetInNaires.
            QuestionSetInNaire newQsin = new QuestionSetInNaire(newQuestionnaire, newQuestionSet,
                    qsDTO.getSequence());
            newQsin.setModifiedBy(currentUser);

            // Add this new QuestionSetInNaire into the new Questionnaire.
            questionSetsInNaire.add(newQsin);
        }

        newQuestionnaire.setQuestionSetsInNaire(questionSetsInNaire);

        newQuestionnaire.setModifiedBy(currentUser);

        newQuestionnaire.setPublishedBy(null);
        newQuestionnaire.setPublishedOn(null);
        newQuestionnaire.setPublishText(null);
        newQuestionnaire.setEffectiveDate(null);
        newQuestionnaire.setExpirationDate(null);
        newQuestionnaire.setRetiredBy(null);
        newQuestionnaire.setRetiredNote(null);
        newQuestionnaire.setRetiredOn(null);

        // Save a new Questionnaire.
        questionnaireDAO.save(newQuestionnaire);
    }

    @Transactional
    @Override
    public void copy(Questionnaire newCopy, List<QuestionSetDTO> qsDTOList) {

        // Get current user.
        User currentUser = userDAO.load(SecurityUtils.getCurrentUser().getUsername());

        newCopy.setQuestionnaireId(null);
        newCopy.setQuestionSetsInNaire(new HashSet<QuestionSetInNaire>());

        for (QuestionSetDTO qsDTO : qsDTOList) {
            QuestionSet questionSet = questionSetDAO.load(qsDTO.getId());
            QuestionSetInNaire newQsin = new QuestionSetInNaire(newCopy, questionSet, qsDTO.
                    getSequence());
            newQsin.setModifiedBy(currentUser);

            newCopy.getQuestionSetsInNaire().add(newQsin);
        }

        newCopy.setModifiedBy(currentUser);

        newCopy.setPublishedBy(null);
        newCopy.setPublishedOn(null);
        newCopy.setPublishText(null);
        newCopy.setEffectiveDate(null);
        newCopy.setExpirationDate(null);

        questionnaireDAO.save(newCopy);
    }
}